package com.gmail.gwanndan.common.hibernate;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration;
import org.hibernate.criterion.Restrictions;

/**
 * 
 * 
 * This class is the abstract parent class for all entities
 * <p>
 * It has some reuseable prototected methods which can be used by other Entities
 * 
 * @author Dennis Ahaus
 * 
 */
public abstract class AbstractEntity implements IEntity {

	private static SessionFactory sessionFactory = null;
	private static Session session;
	private static Configuration configuration;

	public AbstractEntity() {

	}

	public static void setConfiguration(Configuration configuration) {
		AbstractEntity.configuration = configuration;
	}

	public static Configuration getConfiguration() {
		if (configuration == null) {
			configuration = new AnnotationConfiguration().configure();
		}
		return configuration;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.gmail.gwanndan.common.hibernate.IEntity#save()
	 */
	public void save() {
		Session sess = null;
		Transaction trx = null;
		try {
			sess = getSession();
			trx = sess.beginTransaction();
			sess.saveOrUpdate(this);
			trx.commit();
		} catch (HibernateException ex) {
			if (trx != null)
				try {
					trx.rollback();
				} catch (HibernateException exRb) {
				}
			throw new RuntimeException(ex);
		}

	}

	/**
	 * @param clazz
	 * @return
	 */
	public static <T> Criteria getCreateCriteria(Class<T> clazz) {
		return getSession().createCriteria(clazz);
	}

	/**
	 * Close the hibernate session after all work is done
	 * 
	 * @author Dennis Ahaus
	 */
	public static void close() {
		try {
			if (session != null)
				session.close();
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

	/**
	 * Get the {@link SessionFactory} from {@link Hibernate}
	 * <p>
	 * The {@link SessionFactory} is a kind of "storage front facade" to the
	 * persistence layer. With the {@link SessionFactory} you can save, delete,
	 * get or load {@link Object}s into or out of persistence layer.
	 * 
	 * @return {@link SessionFactory}
	 * 
	 * @see {@link Configuration#buildSessionFactory()}
	 * @author Dennis Ahaus
	 */
	public static SessionFactory getSessionFactory() {
		try {
			if (AbstractEntity.sessionFactory == null) {
				sessionFactory = getConfiguration().buildSessionFactory();
			}
			return sessionFactory;
		} catch (HibernateException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

	/**
	 * Get a new {@link Session} for hibernate operations
	 * 
	 * @author Dennis Ahaus
	 * @version 1.0, 04.10.2011
	 * 
	 * @return a new {@link Session}
	 */
	public static Session getSession() {
		if (session == null || !session.isOpen()) {
			session = getSessionFactory().openSession();
		}
		return session;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.gmail.gwanndan.common.hibernate.IEntity#delete()
	 */
	public void delete() {
		Session sess = null;
		try {
			sess = getSession();
			sess.beginTransaction();
			sess.delete(this);
			sess.getTransaction().commit();
		} catch (HibernateException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

	/**
	 * @param crit
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> List<T> find(Criteria crit) {
		try {
			getSession().beginTransaction();
			List<T> list = (List<T>) crit.list();
			getSession().getTransaction().commit();
			return list;
		} catch (HibernateException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

	/**
	 * @param clazz
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> List<T> findAll() {
		Class<T> c = (Class<T>) this.getClass();
		Criteria crit = getCreateCriteria(c);
		return crit.list();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.gmail.gwanndan.common.hibernate.IEntity#deleteAll()
	 */
	@SuppressWarnings("unchecked")
	public void deleteAll() {
		List<IEntity> list = findAll();
		for (IEntity e : list) {
			e.delete();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.gmail.gwanndan.common.hibernate.IEntity#findBy(java.lang.Object,
	 * java.lang.Object)
	 */
	@SuppressWarnings("unchecked")
	public <T> List<T> findBy(Object obj, Object value) {
		Criteria crit = getCreateCriteria(obj.getClass());
		return crit.add(Restrictions.eq(obj.toString(), value)).list();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.gmail.gwanndan.common.hibernate.IEntity#findById(int)
	 */
	public <T> T findById(int id) {
		return findUniqueBy("id", id);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.gmail.gwanndan.common.hibernate.IEntity#findUnique(java.lang.Object,
	 * java.lang.Object)
	 */
	@SuppressWarnings("unchecked")
	public <T> T findUniqueBy(Object obj, Object value) {
		Criteria crit = getCreateCriteria(this.getClass());
		Object o = crit.add(Restrictions.eq(obj.toString(), value))
				.uniqueResult();
		return (T) o;
	}

}
